/*
 * Copyright 2019. Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.android.apps.santatracker.doodles.shared;

import java.util.Stack;

/** A basic 2D vector, with convenience functions to interact with it. */
public class Vector2D {
    private static final int MAX_POOL_SIZE = 50;
    private static final Stack<Vector2D> vectorPool = new Stack<>();

    public float x;
    public float y;

    private Vector2D(float x, float y) {
        this.x = x;
        this.y = y;
    }

    public static Vector2D get() {
        return get(0, 0);
    }

    public static synchronized Vector2D get(float x, float y) {
        if (!vectorPool.isEmpty()) {
            Vector2D v = vectorPool.pop();
            v.set(x, y);
            return v;
        } else {
            return new Vector2D(x, y);
        }
    }

    public static Vector2D get(Vector2D other) {
        return get(other.x, other.y);
    }

    public static float getLength(float x, float y) {
        return (float) Math.sqrt(x * x + y * y);
    }

    /**
     * Release this vector back into the vector pool. Note that, once this has been called, the
     * vector object may be re-used, and there is no guarantee that the released object will act as
     * expected.
     */
    public void release() {
        if (vectorPool.size() < MAX_POOL_SIZE) {
            vectorPool.push(this);
        }
    }

    public Vector2D normalize() {
        float length = getLength();
        if (length == 0) {
            set(0, 0);
        } else {
            set(x / length, y / length);
        }
        return this;
    }

    public Vector2D toNormal() {
        return set(y, -x).normalize();
    }

    public float getLength() {
        return getLength(x, y);
    }

    public Vector2D add(Vector2D rhs) {
        set(this.x + rhs.x, this.y + rhs.y);
        return this;
    }

    public Vector2D add(float x, float y) {
        set(this.x + x, this.y + y);
        return this;
    }

    public Vector2D subtract(Vector2D rhs) {
        set(this.x - rhs.x, this.y - rhs.y);
        return this;
    }

    public Vector2D subtract(float x, float y) {
        set(this.x - x, this.y - y);
        return this;
    }

    public Vector2D scale(float factor) {
        set(this.x * factor, this.y * factor);
        return this;
    }

    public float dot(Vector2D rhs) {
        return x * rhs.x + y * rhs.y;
    }

    public Vector2D rotate(float radians) {
        double cos = Math.cos(radians);
        double sin = Math.sin(radians);
        set((float) (x * cos - y * sin), (float) (x * sin + y * cos));
        return this;
    }

    public Vector2D set(Vector2D other) {
        x = other.x;
        y = other.y;
        return this;
    }

    public Vector2D set(float x, float y) {
        this.x = x;
        this.y = y;
        return this;
    }

    @Override
    public String toString() {
        return "(" + x + ", " + y + ")";
    }

    public float distanceTo(Vector2D other) {
        float dx = x - other.x;
        float dy = y - other.y;
        return (float) Math.sqrt(dx * dx + dy * dy);
    }
}
